﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using CFScript.PInvoke;
using System.IO;

namespace CFScript.Tasks
{
    class Close: Task
    {
        public Close()
        {
            this.name = "<close>";
            this.parameterNames = new string[] { "fullpath", "name", "pid" };
            this.parameterExpandable = new bool[] { true, true, false };
            this.parameterRequired = new bool[] { false, false, false };
            this.parameters = new Dictionary<string, Parameter>();

            Debug.Assert(this.parameterNames.Length == this.parameterRequired.Length &&
                         this.parameterRequired.Length == this.parameterRequired.Length);
        }

        public override InstructionResult Execute(CFScript context)
        {
            InstructionResult result = new InstructionResult();

            // Attributes are mutually exclusive.
            if (this.parameters.Count == 0)
            {
                throw new MissingRequiredParameterException("Parameter 'fullpath', 'name' or 'pid' required");
            }
            else if (this.parameters.Count > 1)
            {
                throw new ScriptExecutionException("<close> task only allows a parameter at the same time. Use just one of 'fullpath', 'name' or 'pid'");
            }
            else
            {
                Process procToClose = null;

                if (this.parameters.ContainsKey("fullpath"))
                {
                    // Kill process by fullpath
                    string fullpath = this.parameters["fullpath"].GetValue(context);

                    try
                    {
                        IntPtr pidPtr = ProcessCE.FindProcessPID(fullpath);
                        procToClose = Process.GetProcessById(pidPtr.ToInt32());
                        
                    }
                    catch (Exception ex)
                    {
                        result.Code = ResultCode.ERROR;
                        result.Message = ex.Message;
                    }

                }
                else if (this.parameters.ContainsKey("name"))
                {
                    // Kill process by name
                    string name = this.parameters["name"].GetValue(context);

                    try
                    {
                        ProcessInfo[] runningProcesses = ProcessCE.GetProcesses();

                        foreach (ProcessInfo pi in runningProcesses)
                        {
                            string piFullpath = pi.FullPath;
                            string piName = Path.GetFileName(piFullpath);
                            string piNameWOExtension = Path.GetFileNameWithoutExtension(piFullpath);

                            if (name.ToLower() == piName.ToLower() || name.ToLower() == piNameWOExtension.ToLower())
                            {
                                procToClose = Process.GetProcessById(pi.Pid.ToInt32());
                                break;
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        result.Code = ResultCode.ERROR;
                        result.Message = ex.Message;
                    }
                }
                else
                {   // phobeous: I guess this task with param. 'pid' won't be very useful but...
                    // Kill process by PID
                    int pid = this.parameters["pid"].GetValueAsInteger();

                    try
                    {
                        procToClose = Process.GetProcessById(pid);
                    }
                    catch (Exception ex)
                    {
                        result.Code = ResultCode.ERROR;
                        result.Message = ex.Message;
                    }
                }

                if (procToClose == null || procToClose.Id == 0)
                {
                    result.Code = ResultCode.ERROR;
                    result.Message = string.Format("Process not found");
                }
                else
                {
                    try
                    {
                        if (procToClose.CloseMainWindow())
                        {
                            result.Code = ResultCode.OK;
                        }
                        else
                        {
                            result.Code = ResultCode.ERROR;
                            result.Message = string.Format("Couldn't close process with PID {0}", procToClose.Id);
                        }
                    }
                    catch(Exception ex)
                    {
                        result.Code = ResultCode.ERROR;
                        result.Message = ex.Message;
                    }
                }
            }

            result.BreakExecution = result.Code == ResultCode.ERROR && this.options.OnError == OnErrorAction.BREAK;

            return result;
        }
    }
}
